
import { AcGameObject } from "./AcGameObject";
import { Enemy } from "./Enemy";
import { Wall } from "./Wall";
import { Tank } from "./Tank";
import { House } from "./House";
// import { Bullet } from "./Bullet";

export class GameMap extends AcGameObject {
    constructor(ctx, parent,store) {//全局变量当成参数传入
        super();//无效参数，因为这里用不到这些参数，但是js里面好像只能有一个构造函数

        this.ctx = ctx;
        this.parent = parent;
        this.store = store;

        this.L = 0;//每一个单元正方体的边长
        this.rows = 21;
        this.cols = 20;

        this.inner_walls_count = 60;
        this.inner_enemy_count = this.cols;
        this.inner_iron_wall_count = 10;

        this.house_col = parseInt(this.cols/2)-2;//老家起始列（行数不用设置，是固定的）
        this.walls = [];//木头墙,铁墙都在里面

        this.enemys = [];//enemys一直在动，不需要启动特判，只要看有没有撞就行
        
        this.opponent_kind=this.store.state.pk.opponent_kind;

        this.my_kind = this.opponent_kind==="playerA"?"playerB":"playerA";

        this.players = [
            //当前玩家总在左下方
            new Tank(this.rows-2,this.house_col-1,this.my_kind,this,this.store),//倒二行中间
            new Tank(1,this.house_col+4,this.opponent_kind,this,this.store)//第二行中间(对称出现)
        ];//两名

        this.current_player = this.players[0].kind===this.my_kind?this.players[0]:this.players[1];//记录当前玩家

        this.bullets = [];
        this.houses = [];

        this.update_model_time = 0;//每二十帧，更新一下游戏元素
        this.model_str = "";
    }



    crete_walls() {
        for(let c = 0;c<this.cols;c++){
            for(let r = 4;r<this.rows-4;r++){
                if(r<parseInt(this.rows/2)){
                    if(c%2===1)this.walls.push(new Wall(r,c,"iron",this))
                    else this.walls.push(new Wall(r,c,"wood",this))
                }
                if(r===parseInt(this.rows/2)){
                    this.walls.push(new Wall(r,c,"wood",this))
                    continue;
                }
                if(r>parseInt(this.rows/2)){
                    if(c%2===0)this.walls.push(new Wall(r,c,"iron",this))
                    else this.walls.push(new Wall(r,c,"wood",this))
                }
            }
        }
        return true;
    }


    add_listening_events() {
        this.ctx.canvas.focus();
        let current_player =this.current_player;
        //本函数内，多次访问的变量，必须搞成临时变量
        // let temp = this.temp;
        let webSocket = this.store.state.pk.socket;
        let d = -1;
        this.ctx.canvas.addEventListener("keydown", e => {

            this.update_model();//每次按下去都更新
            if( current_player.status!=="die"){//暂时不考虑录像功能，不必在后端存所有元素，只要把本玩家的实时操作实时传递到后端然后传给另一个玩家即可
                if (e.key === 'w') {d=0;current_player.set_status("move");current_player.set_direction(0);}
                else if (e.key === 'd'){d=1;current_player.set_status("move"); current_player.set_direction(1);}
                else if (e.key === 's') {d=2;current_player.set_status("move");current_player.set_direction(2);}
                else if (e.key === 'a') {d=3;current_player.set_status("move");current_player.set_direction(3);}
                else if (e.key === ' '){
                    d=-2;//
                    current_player.attack();//当前玩家射击时，需要给后端发送射击操作信息，并送给其他页面玩家做页面同步
                    webSocket.send(JSON.stringify({//向链接发送json转字符串的信息
                        event: "shoot",//确定事件类型
                    }));
                }
                // console.log(this.my_kind+",,"+d+",,"+current_player.status);
                if(d>=0){
                    webSocket.send(JSON.stringify({//向链接发送json转字符串的信息
                        event: "move",//确定事件类型
                        direction: d,
                    }));
                }
            }
        });
        this.ctx.canvas.addEventListener("keyup", function() {
            if(current_player.status!=="die"){
                current_player.set_status("idle");//不设置keyup，坦克会一直走
                d=4;//4表示停止
                console.log("我要停！");
                webSocket.send(JSON.stringify({//向链接发送json转字符串的信息
                    event: "move",//确定事件类型
                    direction: d,
                }));
            }
            // if(player2.status!=="die")player2.set_status("idle");//不设置keyup，坦克会一直走
        });
    }

    crete_enemys(){//同时生成我方和敌方所有ai
        // let g_enemys = [];//地图第一行的列数
        // for(let i=0;i<this.cols;i++)g_enemys[i]=false;//初始化
        // for(let i=0;i< this.inner_enemy_count;i++){
        //     while(i>=0){
        //         let c=parseInt(Math.random()*(this.cols));
        //         if(g_enemys[c])continue;
        //         // let direction = parseInt(Math.random()*4);
        //         g_enemys[c]=true;
        //         this.enemys.push(new Enemy(0,c,"ai",this));//转成canvas格子单位坐标（后续要转像素），在第零行的一个随机不重复位置生成一个敌机
        //         break;
        //     }
        // }
        let cnt = 5;
        for(let i=0;i<this.cols-1;i++){
            cnt--;
            if(cnt<0)break;
            if(i>=this.house_col && i<=this.house_col+3)continue; 
            this.enemys.push(new Enemy(0,i,"ai",this,this.store));//转成canvas格子单位坐标（后续要转像素），在第零行的一个随机不重复位置生成一个敌机
            this.enemys.push(new Enemy(this.rows-1,i,"player_ai",this,this.store));
        }
    }

    crete_houses(){
        this.houses.push(new House(0,this.house_col+1,this.opponent_kind,this));//敌方老家

        this.walls.push(new Wall(0, this.house_col,"wood",this));
        this.walls.push(new Wall(1, this.house_col,"wood",this));
        this.walls.push(new Wall(2, this.house_col,"wood",this));
        this.walls.push(new Wall(2, this.house_col+1,"wood",this));
        this.walls.push(new Wall(2, this.house_col+2,"wood",this));
        this.walls.push(new Wall(2, this.house_col+3,"wood",this));
        this.walls.push(new Wall(1, this.house_col+3,"wood",this));
        this.walls.push(new Wall(0, this.house_col+3,"wood",this));

        this.houses.push(new House(this.rows-2,this.house_col+1,this.my_kind,this));//我方老家

        this.walls.push(new Wall(this.rows-1, this.house_col,"wood",this));
        this.walls.push(new Wall(this.rows-2, this.house_col,"wood",this));
        this.walls.push(new Wall(this.rows-3, this.house_col,"wood",this));
        this.walls.push(new Wall(this.rows-3, this.house_col+1,"wood",this));
        this.walls.push(new Wall(this.rows-3, this.house_col+2,"wood",this));
        this.walls.push(new Wall(this.rows-3, this.house_col+3,"wood",this));
        this.walls.push(new Wall(this.rows-2, this.house_col+3,"wood",this));
        this.walls.push(new Wall(this.rows-1, this.house_col+3,"wood",this));

    }




    update_size() {
        this.L = parseInt(Math.min(this.parent.clientWidth / this.cols, this.parent.clientHeight / this.rows));
        this.ctx.canvas.width = this.L * this.cols;
        this.ctx.canvas.height = this.L * this.rows;
    }

    //这四个判断函数，传入都是子弹当前位置，碰撞物也只要看当前位置
    check_boundary_cross(cell){ // 检测当前坦克位置是否出界了
        if(cell.r<0 || cell.r>this.rows-cell.height || cell.c<0 || cell.c>this.cols-cell.width)return true;
        //注意：：这里加入height和width是为了使得坦克能够到达底部
        return false;
    }

    check_player_hit(cell){ // 检测玩家是否被打中（具体被谁的子弹打中，在Bullet.js页面判断），被打中，就返回玩家
        for(let player of this.players)
            if(this.check_overlay(cell,player.cell) ){//只判当前位置
                return player;
            }
        return null;
    }

    check_house_hit(cell){ // 检测老家是否被打中
        for(let house of this.houses)
            if(this.check_overlay(cell,house) ){//house本身可以看作cell
                if(house.kind===this.my_kind){
                    this.store.commit("updateLoser", this.my_kind);
                }else{
                    this.store.commit("updateLoser", this.opponent_kind);
                }
            }
        // return null;
    }

    check_ai_hit(cell){ // 检测ai是否被打中（具体被谁的子弹打中，在Bullet.js页面判断）就返回这个ai
        //被玩家打中，才要删除这名ai，但是这里并不知道子弹的主人，所以要在Bullet实现可能存在的删除操作
        for(let enemy of this.enemys){//以当前坦克的下一步位置为基准，和其他坦克的当前位置和下一步位置去判断
            if(this.check_overlay(cell,enemy.cell)){

                return enemy;
            }
        }
        return null;
    }
    check_wall_hit(cell){ // 检测当前坦克的下一步位置是否撞墙了，如果撞了，就返回这个墙，并从墙列表中删除，否则返回空
        for (let wall of this.walls) {
            if(wall.status==="idle")//因为死墙没有摧毁，没有从列表删除，所以要判断
                if (this.check_overlay(cell,wall)){//这里因为wall本身可以看成是Cell类
                    return wall;//表示撞实了,返回墙，要在子弹类里面进行撞之后的操作，因为这是一个复用的类，坦克撞墙也调用了
                }
        }
        return null;
    }
    check_bullet_hit(cell){//检测子弹被另一阵营子弹撞了(在子弹类中判断)
        for(let bullet of this.bullets){
            if(this.check_overlay(cell,bullet.cell))
                return bullet;
        }
        return null;
    }
    
    check_overlay(A,B){//检测AB有没有碰在一起（只要AB有x,y,width,height参数，就能进来比较）
        //(先判断没碰着时的情况，然后再取反)
        //依次是：A左B右，B左A右，A上B下，A下B上
        let state = (A.c + A.width) < (B.c)
        ||(A.c) >( B.c + B.width) ||
        (A.r + A.height) < (B.r) ||
        (A.r) > (B.r + B.height);
        return !state;//取反
    }
    
    //现在只要传入当前位置
    check_valid(cell,tank_kind) {  // 检测当前坦克位置是否合法

        // if(tank_kind===this.opponent_kind){//如果时映射的敌方坦克的移动环节，不需要在判断碰撞，因为映射前的坦克已经判断过了
        //     return true;
        // }
        if(this.check_boundary_cross(cell))return false;

        if(this.check_wall_hit(cell))return false;

        if(this.check_house_hit(cell))return false;

        if(tank_kind && tank_kind.substring(0,6) === "player"){//玩家碰ai
            for(let enemy of this.enemys){//以当前坦克的下一步位置为基准，和其他坦克的当前位置和下一步位置去判断
                if(this.check_overlay(cell,enemy.cell)){
                    return false;
                }
            }

            if(this.players){
                if(tank_kind===this.my_kind){
                    let opponent = this.players[1];
                    //我方坦克碰敌方坦克（被映射的坦克）
                    if(opponent && this.check_overlay(cell,opponent.cell))return false;
                }
                if(tank_kind===this.opponent_kind){
                    //敌方坦克（被映射的坦克）碰我方坦克
                    let my = this.players[0];
                    if(my && this.check_overlay(cell,my.cell))return false;
                }
                
            }
        }else{//ai碰玩家
            for(let player of this.players)
            if(this.check_overlay(cell,player.cell) ){//只判当前位置
                return false;
            }
        }
        return true;
    }


    update_model(){//每隔二十帧，更新除子弹外所有游戏元素的位置和状态信息
        let model_str = this.model_str;//墙只传送生死状态信息"die"和"idle"

        for(let wall of this.walls){

            if(wall.status === "idle"){
                model_str+="0"//表示静止
            }
            else {
                model_str+="1";
            }
        }

        model_str+="#";//表示分割

        for(let player of this.players){//所有玩家页面都是先本方后敌方
            if(player.status==="idle"){
                model_str+="0";//静止
            }
            else if(player.status==="die")model_str+="1";//死亡
            else{
                model_str+="2";//移动
            }
            model_str+="#";
            model_str+=player.cell.r.toFixed(2)+"#";//行列坐标
            model_str+=player.cell.c.toFixed(2)+"#";
        }

        this.store.state.pk.socket.send(JSON.stringify({//向链接发送json转字符串的信息
            event: "updateModel",//确定事件类型
            model: model_str,
        }));

    }

    start() {
        this.update_size();//每次都要更新大小，因为窗口可能在不断缩小放大
        this.crete_walls();//因为创建墙，要用到this.L，所以this.update_size();要在前面执行一次；
        // this.crete_enemys();//生成地方坦克
        this.crete_houses();
        this.add_listening_events();
    }
    update() {
        this.update_size();//每次都要更新大小，因为窗口可能在不断缩小放大
        // console.log(this.current_player.status);

        this.update_model_time++;
        if(this.update_model_time>=20){
            this.update_model();//每20帧更新一下游戏元素
            this.update_model_time=0;//重置时间
        }

        this.render();
    }

    render() {
        const color_even = "#AAD752", color_odd = "#A2D048";
        for (let r = 0; r < this.rows; r++) {
            for (let c = 0; c < this.cols; c++) {
                if ((r + c) % 2 == 0) {
                    this.ctx.fillStyle = color_even;
                } else {
                    this.ctx.fillStyle = color_odd;
                }
                this.ctx.fillRect(c * this.L, r * this.L, this.L, this.L);
            }
        }
    }
}
